package com.codrim.cs.data.server.dao.realtime;

import com.codrim.cs.data.server.dao.param.RealTimeDataSearchParam;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;

import lombok.Data;

/**
 * 将redis`advId_affId_offerId_hour`转化为对象
 */
@Data
public class RealTimeKey {
    private static final DateTimeFormatter UTC_HOUR_FORMATTER = DateTimeFormatter
            .ofPattern("yyyyMMddHH").withZone(ZoneId.of("UTC"));

    private int advId;
    private int affId;
    private int offerId;
    private int hourTime;

    boolean isMatchForAdvertiser(int advertiserId, Instant begin, Instant end) {
        int beginHourTime = NumberUtils.toInt(UTC_HOUR_FORMATTER.format(begin));
        int endHourTime = NumberUtils.toInt(UTC_HOUR_FORMATTER.format(end));
        return this.advId == advertiserId && beginHourTime <= hourTime && endHourTime >= hourTime;
    }

    boolean isMatchForAffiliate(int affiliateId, Instant begin, Instant end) {
        return this.affId == affiliateId && matchForTimePeriod(begin, end);
    }

    private boolean matchForTimePeriod(Instant begin, Instant end) {
        int beginHourTime = NumberUtils.toInt(UTC_HOUR_FORMATTER.format(begin));
        int endHourTime = NumberUtils.toInt(UTC_HOUR_FORMATTER.format(end));
        return beginHourTime <= hourTime && endHourTime >= hourTime;
    }

    boolean isMatch(List<RealTimeGroupByField> fields, RealTimeDataSearchParam param, Instant begin, Instant end) {
        return CollectionUtils.isNotEmpty(fields) && param != null
                && fields.stream().filter(field -> field != null).allMatch(field -> matchForField(param, field))
                && matchForTimePeriod(begin, end);
    }

    private boolean matchForField(RealTimeDataSearchParam param, RealTimeGroupByField field) {
        Integer valueFromRealTime = Optional.ofNullable(field).map(f -> f.getValueFromRealTimeKey).map(f -> f.apply(this)).orElse(null);
        Integer valueFromParam = Optional.ofNullable(field).map(f -> f.getValueFromSearchParam).map(f -> f.apply(param)).orElse(null);
        return valueFromRealTime != null && valueFromParam != null && valueFromRealTime.intValue() == valueFromParam.intValue();
    }

    static RealTimeKey fromKey(String key) {
        Matcher matcher = RealTimeDataDaoImpl.KEY_PATTERN.matcher(key);
        if (!matcher.matches()) {
            return null;
        }

        matcher.reset();
        if (matcher.find()) {
            RealTimeKey result = new RealTimeKey();
            result.advId = NumberUtils.toInt(matcher.group(1));
            result.affId = NumberUtils.toInt(matcher.group(2));
            result.offerId = NumberUtils.toInt(matcher.group(3));
            result.hourTime = NumberUtils.toInt(matcher.group(4));
            return result;
        }

        return null;
    }

    static RealTimeKey fromKey(String key, List<RealTimeGroupByField> fields) {
        RealTimeKey fullRealTimeKey = fromKey(key);
        assert fullRealTimeKey != null;
        RealTimeKey result = new RealTimeKey();
        fields.forEach(field -> Optional.ofNullable(field)
                .ifPresent(f -> f.setValue.accept(result, Optional.ofNullable(f.getValueFromRealTimeKey)
                        .map(rtk -> rtk.apply(fullRealTimeKey))
                        .orElse(null))));
        result.hourTime = fullRealTimeKey.hourTime;
        return result;
    }

}
